今天來說說我們的 Bot
,也就是框架的部分是如何來處理模組的。
.use
我們從最簡單的骨架開始,Bot
中會擁有一個 discord.js 的 client
,以及多個模組 modules
。
如果 .use
被呼叫,我們就把新的模組放到 modules
陣列裡。當 .start
被呼叫時,我們就用先前加入的模組聲明的 intents
來初始化 client
並登入。
export class Bot {
client: Client = new Client({ intents: [] });
modules: Module[] = [];
use(...modules: Module[]): this {
this.modules.push(...modules);
return this;
}
async start(): Promise<void> {
this.client.options.intents = [
...new Set(this.modules.flatMap((module) => module.intents ?? [])),
];
await this.client.login(process.env.BOT_TOKEN);
}
}
Module
目前,我們的模組只處理文字的輸入,所以我只先定義了 message
和 dm
兩種事件方法。
因為底層是 discord.js,所以之後應該會對於所有的事件都有對應的方法(當然是可選的)。
export interface Module {
name: string;
intents?: GatewayIntentBits[];
init?(bot: Bot): Promise<void>;
message?(message: Message<true>, next: CallNextModule): Promise<void>;
dm?(message: Message<false>, next: CallNextModule): Promise<void>;
}
intents
用來聲明模組需要的意圖。
init
會在 Bot
登入後被呼叫,我們可以在這裡做一些初始化的工作,例如更新資料庫、下載檔案等。
message
和 dm
方法會在 Bot
收到訊息時被呼叫,我們可以在這裡做一些處理,例如回應訊息、處理指令等。
在這裡我參考了一下 Koa Compose 聰明的實作方式。
首先,我們在所有模組初始化完之後,會用建立一個 .shortcut.<event>
來儲存有監聽該事件的模組。
async start(): Promise<void> {
// ...
this.shortcut.message = this.modules.filter(
(module) => module["message"],
) as Required<Module>[];
// ...
}
然後,我們把所有 shortcut
內的對應事件方法,包裝成一個 pipeline
。
這裡有簡單做了一些錯誤處理,例如多次呼叫 downstream 拋出錯誤,以及最內層的 next
會直接回傳。
async start(): Promise<void> {
// ...
this.pipeline.message = (message: Message<true>) => {
let idx = -1;
const execute = async (i: number): Promise<void> => {
if (i <= idx) {
throw new Error("handler already called.");
}
if (i >= this.shortcut.message.length) {
return;
}
idx = i;
return await this.shortcut.message[i].message(
message,
execute.bind(this, i + 1),
);
};
return execute(0);
};
}
在 Bot
收到訊息時,呼叫相應 pipeline
進行處理,這樣各模組就可以在內部呼叫它的 downstream 模組。
this.client.on("messageCreate", async (message) => {
if (message.author.id === this.client.user?.id) {
return;
}
if (message.guild) {
await this.pipeline.message(message);
} else {
await this.pipeline.dm(message);
}
});
完成到這裡,昨天的兩個模組就可以在洋蔥模型下正常運作了。
接下來,應該來談談如何處理 slash command 了。我個人蠻喜歡 commander
的,所以想看看能不能用類似的方式來處理 slash command。
今天拿
Ping
改一改寫了Echo
模組,可以用pnpm i pure-cat-module-echo
然後.use(new Echo())
試試喔。
以 2022/09/20 20:00 ~ 2022/09/21 20:00 文章觀看數增加值排名
誤差: 1 小時
+630
「全端挑戰」了解Scss與React Component與首頁概念圖與UI實作
+620
「全端挑戰」Scss與React Component的動態實作Navbar與Header
+598
「全端挑戰」製作動態網站第一步從了解useState與它的用法開始
+575
「全端挑戰」學習Mern全端開發概念與動態網站開發流程懶人包
+412
學徒、忍者、大師? 忍蛋三人組的火影之路 (Day 0)
+382
D01 - 沒時間解釋了,快上車!
+306
Introduction of Vue, Django, MongoDB, Nginx
+249
D06 - 打造遊戲選單按鈕
+234
D04 - 門面怎麼可以沒有背景
+204
[DAY-01] - Julia 介紹與引言大綱
SamKo 的 MERN 系列依舊佔滿榜上前幾名的位置,我覺得文中的圖做的很好,值得一看。